/*!
* @file    image_otsu.c
* @version v1.0
* @date    2018-02-05
* @author  苏勇 suyong_yq@126.com
* @brief   实现对灰度图像使用最大类间方差取动态阈值
*/

#include "image.h"

/* 使用大津法（最大类间方差法）计算灰度图像的最佳动态阈值
* 原理：
* 最大类间方差法是由日本学者大津于1979年提出的,是一种自适应的阈值确定的方法,又叫大津法,简称OTSU。
* 它是按图像的灰度特性,将图像分成背景和目标2部分。背景和目标之间的类间方差越大,说明构成图像的2部分的差别越大,
* 当部分目标错分为背景或部分背景错分为目标都会导致2部分差别变小。
* 因此,使类间方差最大的分割意味着错分概率最小。
* 本函数返回可作为阈值的灰度值
*/
uint8_t Image_GetThreshold_OTSU(uint8_t *pixels, uint32_t pixelCount)
{
    uint32_t i;
    uint32_t pixelNum[256] = {0}; /* 设定灰度为8b分辨率 */
    uint32_t sum; /* 灰度质量距总和 */
    uint32_t csum;/* 前景色的灰度质量距总和 */
    uint32_t n1, n2; /* n1为前景点数，n2位背景点数 */
    double m1, m2; /* 前景和背景的平均灰度值 */
    double sb; /* 类方差 */
    double smax;
    uint32_t k;
		uint8_t kth;

    /* 生成直方图，统计每个灰度上像素点的数量 */
    for (i = 0U; i < pixelCount; i++)
    {
        pixelNum[pixels[i]]++; /* 遍历每个像素，在像素对应灰度点的计数值上累加 */
    }

    /* 计算质量距总和，为后续计算做准备 */
    sum = 0U;
    for (k = 0U; k < 256U; k++)
    {
        sum += k * pixelNum[k];
    }

    n1 = 0U;
    csum = 0U;
    smax = (double)(-1);
    kth = 0U;
    for (k = 0U; k < 256U; k++)
    {
        n1 += pixelNum[k]; /* n1累加了当前为前景色的总点数 */
        if (n1 == 0U) /* 暂时未遇到划分阈值的灰度值 */
        {
            continue;
        }
        n2 = pixelCount - n1;
        if (n2 == 0U) /* 如果n2没有像素点，则说明所有像素同色 */
        {
            break;
        }
        csum += k * pixelNum[k]; /* 累加到当前灰度的前景像素质量距总和 */
        m1 = ((double)(csum))/((double)(n1));     /* m1为前景色平均灰度 */
        m2 = ((double)(sum-csum))/((double)(n2)); /* m2为前景色平均灰度 */

        /* 计算类方差 */
        sb = n1 * n2 * (m1 - m2) * (m1 - m2);
        if (sb > smax) /* 捕获最大类方差 */
        {
            smax = sb;
            kth = k;
        }
    }
    return kth;
}
